/*******************************************************************************
 *                                    ZLG
 *                         ----------------------------
 *                         innovating embedded platform
 *
 * Copyright (c) 2001-present Guangzhou ZHIYUAN Electronics Co., Ltd.
 * All rights reserved.
 *
 * Contact information:
 * web site:    https://www.zlg.cn
 *******************************************************************************/
#ifndef __HC32F4A0_QSPI_H__
#define __HC32F4A0_QSPI_H__

#ifdef __cplusplus
extern "C" {
#endif  /* __cplusplus*/
#include "core/include/hc32f4a0_regs_qspi.h"
#include "core/include/hc32f4a0_regs_gpio.h"
#include "driver/gpio/hc32f4a0_gpio.h"

/* \brief QSPI 访问外部闪存空间地址*/
#define QSPI_ROM_MAP_BASE                   (0x98000000UL)

/* \brief QSPI 通讯模式*/
#define QSPI_COMM_ROM_ACCESS                (0x0U)                  /* 内存访问模式 */
#define QSPI_COMM_DIRECT_COMM               (QSPI_CR_DCOME)         /* 直接通信模式 */

/* \brief QSPI 写保护引脚电平*/
#define QSPI_WP_LOW                         (0x0U)                  /* WP(QIO2) 输出低电平 */
#define QSPI_WP_HIGH                        (QSPI_FCR_WPOL)         /* WP(QIO2) 输出高电平 */

/* \brief 预读取功能设置*/
#define QSPI_PREFETCH_DISABLE               (0x0U)                  /* 关闭预读取功能 */
#define QSPI_PREFETCH_ENABLE                (QSPI_CR_PFE)           /* 使能预读取功能 */

/* \brief 预读取即时停止许可设置*/
#define QSPI_PREFETCH_STOP_BYTE_EDGE        (0x0U)                  /* 当前的预读取动作在字节边界中止   */
#define QSPI_PREFETCH_STOP_IMMED            (QSPI_CR_PFSAE)         /* 当前的预读取动作即时中止  */

/* \brief SPI 模式*/
#define QSPI_SPI_MODE_0                     (0x0U)                  /* 模式0 */
#define QSPI_SPI_MODE_3                     (QSPI_CR_SPIMD3)        /* 模式3 */

/* \brief QSPI 指令模式*/
#define QSPI_INSTR_1LINE                    (0x0U)                  /* 扩展式指令 */
#define QSPI_INSTR_2LINE                    (QSPI_CR_IPRSL_0)       /* 二线式指令 */
#define QSPI_INSTR_4LINE                    (QSPI_CR_IPRSL_1)       /* 四线式指令 */

/* \brief QSPI 地址模式*/
#define QSPI_ADDR_1LINE                     (0x0U)                  /* 扩展式地址 */
#define QSPI_ADDR_2LINE                     (QSPI_CR_APRSL_0)       /* 二线式地址 */
#define QSPI_ADDR_4LINE                     (QSPI_CR_APRSL_1)       /* 四线式地址 */

/* \brief QSPI 数据模式*/
#define QSPI_DATA_1LINE                     (0x0U)                  /* 扩展式数据 */
#define QSPI_DATA_2LINE                     (QSPI_CR_DPRSL_0)       /* 二线式数据 */
#define QSPI_DATA_4LINE                     (QSPI_CR_DPRSL_1)       /* 四线式数据 */

/* \brief QSPI 地址宽度*/
#define QSPI_ADDR_WIDTH_1BYTE               (0x0U)                  /* QSPI 地址宽度为1字节 */
#define QSPI_ADDR_WIDTH_2BYTE               (QSPI_FCR_AWSL_0)       /* QSPI 地址宽度为2字节 */
#define QSPI_ADDR_WIDTH_3BYTE               (QSPI_FCR_AWSL_1)       /* QSPI 地址宽度为3字节 */
#define QSPI_ADDR_WIDTH_4BYTE               (QSPI_FCR_AWSL)         /* QSPI 地址宽度为4字节 */

/* \brief QSSN 有效时间延长设置*/
#define QSPI_CS_EXTEND_0CYCLE               (0x0U)                  /* 不延长 QSSN有效时间 */
#define QSPI_CS_EXTEND_32CYCLE              (QSPI_CSCR_SSNW_0)      /* 将 QSSN有效时间延长 有效时间延长 32个QSCK周期  */
#define QSPI_CS_EXTEND_128CYCLE             (QSPI_CSCR_SSNW_1)      /* 将 QSSN有效时间延长 有效时间延长 128个QSCK周期  */
#define QSPI_CS_EXTEND_INFINITE             (QSPI_CSCR_SSNW)        /* 将 QSSN有效时间无限延长 有效时间无限延长 */

/* \brief QSSN 信号释放时间延迟设置*/
#define QSPI_CS_RELEASE_AFTER_0P5_CYCLE     (0x0U)                  /* 比 QSCK最后 一个上升沿滞最后 一个上升沿滞最后 一个上升沿滞0.5个QSCK释放 QSSN */
#define QSPI_CS_RELEASE_AFTER_1P5_CYCLE     (QSPI_FCR_SSNHD)        /* 比 QSCK最后 一个上升沿滞最后 一个上升沿滞最后 一个上升沿滞1.5个QSCK释放 QSSN */

/* \brief QSSN 信号输出时间延迟设置*/
#define QSPI_CS_SETUP_BEFORE_0P5_CYCLE      (0x0U)                  /* 比 QSCK第一个上升沿提前 第一个上升沿提前 0.5个QSCK输出 QSSN */
#define QSPI_CS_SETUP_BEFORE_1P5_CYCLE      (QSPI_FCR_SSNLD)        /* 比 QSCK第一个上升沿提前 第一个上升沿提前 1.5个QSCK输出 QSSN */

/* \brief QSPI 读模式*/
#define QSPI_READ_STANDARD_READ             (0x0UL)                         /* 标准读模式*/
#define QSPI_READ_FAST_READ                 (0x1UL << QSPI_CR_MDSEL_POS)    /* 快速读模式 */
#define QSPI_READ_FAST_READ_DUAL_OUTPUT     (0x2UL << QSPI_CR_MDSEL_POS)    /* 二线式输出快速读模式 */
#define QSPI_READ_FAST_READ_DUAL_IO         (0x3UL << QSPI_CR_MDSEL_POS)    /* 二线式输入输出快速读模式 */
#define QSPI_READ_FAST_READ_QUAD_OUTPUT     (0x4UL << QSPI_CR_MDSEL_POS)    /* 四线式输出快速读模式 */
#define QSPI_READ_FAST_READ_QUAD_IO         (0x5UL << QSPI_CR_MDSEL_POS)    /* 四线式输入输出快速读模式 */
#define QSPI_READ_CUSTOM_STANDARD_READ      (0x6UL << QSPI_CR_MDSEL_POS)    /* 自定义标准读模式 */
#define QSPI_READ_CUSTOM_FAST_READ          (0x7UL << QSPI_CR_MDSEL_POS)    /* 自定义快速读模式 */

/* \brief 检查时钟分频参数*/
#define IS_QSPI_CLK_DIV(x)                (((x) >= 2) && ((x) <= 64))

#define IS_QSPI_CS_SETUP_TIMING(x)        (((x) == QSPI_CS_SETUP_BEFORE_0P5_CYCLE) || \
                                           ((x) == QSPI_CS_SETUP_BEFORE_1P5_CYCLE))

#define IS_QSPI_CS_RELEASE_TIMING(x)      (((x) == QSPI_CS_RELEASE_AFTER_0P5_CYCLE) || \
                                           ((x) == QSPI_CS_RELEASE_AFTER_1P5_CYCLE))

#define IS_QSPI_CS_IDLE_TIME(x)           (((x) >= 1U) && ((x) <= 16U))

#define IS_QSPI_CS_EXTEND_TIME(x)         (((x) == QSPI_CS_EXTEND_0CYCLE) ||   \
                                           ((x) == QSPI_CS_EXTEND_32CYCLE)     ||   \
                                           ((x) == QSPI_CS_EXTEND_128CYCLE)    ||   \
                                           ((x) == QSPI_CS_EXTEND_INFINITE))

#define IS_QSPI_SPI_MODE(x)               (((x) == QSPI_SPI_MODE_0) || ((x) == QSPI_SPI_MODE_3))

#define IS_QSPI_PREFETCH_STOP_POSITION(x) (((x) == QSPI_PREFETCH_STOP_BYTE_EDGE) || \
                                           ((x) == QSPI_PREFETCH_STOP_IMMED))

#define IS_QSPI_PREFETCH_CMD(x)           (((x) == QSPI_PREFETCH_ENABLE) || \
                                           ((x) == QSPI_PREFETCH_DISABLE))
/* \brief 检查写保护引脚电平参数*/
#define IS_QSPI_WP_LEVEL(x)               (((x) == QSPI_WP_LOW) || ((x) == QSPI_WP_HIGH))

#define IS_QSPI_READ_MODE(x)              (((x) == QSPI_READ_STANDARD_READ)         ||   \
                                           ((x) == QSPI_READ_FAST_READ)             ||   \
                                           ((x) == QSPI_READ_FAST_READ_DUAL_OUTPUT) ||   \
                                           ((x) == QSPI_READ_FAST_READ_DUAL_IO)     ||   \
                                           ((x) == QSPI_READ_FAST_READ_QUAD_OUTPUT) ||   \
                                           ((x) == QSPI_READ_FAST_READ_QUAD_IO)     ||   \
                                           ((x) == QSPI_READ_CUSTOM_STANDARD_READ)  ||   \
                                           ((x) == QSPI_READ_CUSTOM_FAST_READ))

#define IS_QSPI_COMM_MODE(x)              (((x) == QSPI_COMM_ROM_ACCESS) || ((x) == QSPI_COMM_DIRECT_COMM))

#define IS_QSPI_ADDR_WIDTH(x)             (((x) == QSPI_ADDR_WIDTH_1BYTE)  ||    \
                                           ((x) == QSPI_ADDR_WIDTH_2BYTE)  ||    \
                                           ((x) == QSPI_ADDR_WIDTH_3BYTE)  ||    \
                                           ((x) == QSPI_ADDR_WIDTH_4BYTE))

#define IS_QSPI_DUMMY_CYCLES(x)           (((x) >= 3U) && ((x) <= 18U))

#define IS_QSPI_INSTR_MODE(x)             (((x) == QSPI_INSTR_1LINE)   ||  \
                                           ((x) == QSPI_INSTR_2LINE)   ||  \
                                           ((x) == QSPI_INSTR_4LINE))

#define IS_QSPI_ADDR_MODE(x)              (((x) == QSPI_ADDR_1LINE) ||   \
                                           ((x) == QSPI_ADDR_2LINE) ||   \
                                           ((x) == QSPI_ADDR_4LINE))

#define IS_QSPI_DATA_MODE(x)              (((x) == QSPI_DATA_1LINE) ||   \
                                           ((x) == QSPI_DATA_2LINE) ||   \
                                           ((x) == QSPI_DATA_4LINE))

/* \brief QSPI 驱动配置*/
struct qspi_drv_cfg {
    char       *p_name;
    uint8_t     sck_port;
    uint16_t    sck_pin;
    uint8_t     sck_func;

    uint8_t     nss_port;
    uint16_t    nss_pin;
    uint8_t     nss_func;

    uint8_t     io0_port;
    uint16_t    io0_pin;
    uint8_t     io0_func;

    uint8_t     io1_port;
    uint16_t    io1_pin;
    uint8_t     io1_func;

    uint8_t     io2_port;
    uint16_t    io2_pin;
    uint8_t     io2_func;

    uint8_t     io3_port;
    uint16_t    io3_pin;
    uint8_t     io3_func;
};

/* \brief QSPI 配置结构体*/
struct hc32f4a0_qspi_cfg {
    uint32_t clk_div;             /* QSPI时钟分频系数， QSCK = HCLK / ClkDiv，此参数范围为2~64*/
    uint32_t cs_setup_timing;     /* QSSN 信号输出时间延迟设置*/
    uint32_t cs_release_timing;   /* QSSN 信号释放时间延迟设置*/
    uint32_t cs_idle_time;        /* QSSN 最小无效时间设置， 在命令之间 QSSN 需要保持空闲(高电平)几个周期*/
    uint32_t cs_extend_time;      /* QSSN 有效时间延长设置，QSPI 总线访问后 QSSN 有效时间延长功能*/
    uint32_t spi_mode;            /* SPI 模式，SPI 模式0与模式3的区别在于待机时的 QSCK 信号，待机时模式0的 QSCK信号为低电平，模式3为高电平*/
    uint32_t prefetch_cmd;        /* 预读取功能*/
    uint32_t prefetch_stop_pos;   /* 预读取即时停止许可*/
    uint32_t wp_level;            /* WP 管脚输出电平设置*/
    uint32_t comm_mode;           /* 通信模式选择*/
    uint32_t addr_width;          /* 地址宽度*/
    uint32_t instr_mode;          /* 指令模式*/
    uint32_t addr_mode;           /* 地址模式*/
    uint32_t data_mode;           /* 数据模式*/
    uint32_t read_mode;           /* 读取模式*/
    uint8_t  rom_access_instr;    /* ROM 访问指令，只支持 QSPI FLASH读指令*/
    uint32_t dummy_cycles;        /* 虚拟周期设置*/
};

/**
 * \brief 进入  QSPI 直接通讯模式
 */
static inline void qspi_direct_comm_mode_enter(void){
    /* 设置标准读模式 */
    CLEAR_REG32_BIT(HC32F4A0_QSPI->CR, QSPI_CR_MDSEL);
    /* 进入直接通讯模式 */
    SET_REG32_BIT(HC32F4A0_QSPI->CR, QSPI_CR_DCOME);
}

/**
 * \brief 退出 QSPI 直接 通讯模式
 */
static inline void qspi_direct_comm_mode_exit(void){
    CLEAR_REG32_BIT(HC32F4A0_QSPI->CR, QSPI_CR_DCOME);
}

/**
 * \brief  QSPI 直接通讯模式写数据
 */
static inline void qspi_direct_comm_val_write(uint8_t Val){
    WRITE_REG32(HC32F4A0_QSPI->DCOM, Val);
}

/**
 * \brief  QSPI 直接通讯模式读数据
 */
static inline uint8_t qspi_direct_comm_val_read(void){
    return (uint8_t)HC32F4A0_QSPI->DCOM;
}

/**
 * \brief QSPI 设置读模式
 */
void QSPI_ReadModeSet(uint32_t ReadMode, uint8_t ReadInstr, uint32_t DummyCycles);
/**
 * \brief QSPI 结构体初始化
 */
void qspi_struct_init(struct hc32f4a0_qspi_cfg *p_qspi_cfg);
/**
 * \brief 初始化 QSPI 配置
 */
int qspi_cfg(struct hc32f4a0_qspi_cfg *p_qspi_cfg);
/**
 * \brief 初始化 QSPI
 */
int qspi_drv_init(void);
/**
 * \brief QSPI 读数据
 *
 * \param[in] address   要读的地址
 * \param[in] p_dest    数据缓存
 * \param[in] dest_size 要读的数据大小
 *
 * \retval 成功返回0
 */
int qspi_data_read(uint32_t address, uint8_t *p_dest, uint32_t dest_size);
/**
 * \brief QSPI 写数据
 *
 * \param[in] instr    指令
 * \param[in] address  要写的地址
 * \param[in] p_src    数据缓存
 * \param[in] size     要写的数据大小
 *
 * \retval 成功返回0
 */
int qspi_data_write(uint32_t       instr,
                    uint32_t       address,
                    const uint8_t *p_src,
                    uint32_t       size);
#ifdef __cplusplus
}
#endif  /* __cplusplus  */
#endif
